package core

import (
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"time"

	"gitee.com/huanghua_2017/blade/api"
	"gitee.com/huanghua_2017/blade/state"
	"gitee.com/huanghua_2017/hggutils"
	"gitee.com/huanghua_2017/hggutils/jsonrpc"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	"go.uber.org/fx"
)

// config ...
type config struct {
	Listen string
	CA     struct {
		KeyFile  string
		CertFile string
	}
}

// Core ...
type Core struct {
	fx.In

	Logger *log.Logger
	Cfg    *config
	State  *state.StatusStruct

	RPCServer *jsonrpc.RPCServer
	Svr       *http.Server
}

// ListenAndServe ...
func (slf *Core) ListenAndServe() error {
	var svrapi api.Server
	svrapi = slf
	slf.RPCServer.Register("Blade", svrapi)

	r := mux.NewRouter()
	r.HandleFunc("/ws", slf.websocketHandler)
	r.Handle("/rpc/v0", slf.RPCServer)
	r.PathPrefix("/").Handler(http.DefaultServeMux) // pprof
	slf.Svr.Handler = r

	if slf.Cfg.CA.CertFile != "" && slf.Cfg.CA.KeyFile != "" {
		return slf.Svr.ListenAndServeTLS(slf.Cfg.CA.CertFile, slf.Cfg.CA.KeyFile)
	}
	return slf.Svr.ListenAndServe()
}

// websocketHandler websocket处理流程
func (slf *Core) websocketHandler(rw http.ResponseWriter, req *http.Request) {
	conn, err := (&websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true
		}}).Upgrade(rw, req, nil)
	if err != nil {
		return
	}

	go slf.readFristRequest(conn)
}

func StartServer() error {
	newLog := func() *log.Logger {
		fout, _ := os.Create("log-server.log")
		return log.New(fout, "", log.Lshortfile|log.LstdFlags)
	}
	newConfig := func(logger *log.Logger) (*config, error) {
		c := &config{}
		err := hggutils.ReadJSONFile("config.json", c)
		return c, err
	}
	newrpc := func() *jsonrpc.RPCServer {
		return jsonrpc.NewServer()
	}
	newsrv := func(cfg *config) *http.Server {
		return &http.Server{
			Addr:           cfg.Listen,
			ReadTimeout:    10 * time.Second,
			WriteTimeout:   10 * time.Second,
			MaxHeaderBytes: 1 << 20,
		}
	}
	var c Core
	app := fx.New(
		fx.Provide(
			newLog,
			newConfig,
			newrpc,
			newsrv,
			state.NewState,
		),
		fx.Populate(&c),
	)

	startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := app.Start(startCtx); err != nil {
		return err
	}

	ioutil.WriteFile("sapi", []byte(c.Cfg.Listen), 0644)
	defer os.Remove("sapi")
	c.Logger.Println("开始监听: ", c.Cfg.Listen)
	return c.ListenAndServe()
}
